From: Keir Fraser Date: Mon, 18 Apr 2011 12:36:10 +0000 (+0100) Subject: svm: implement instruction fetch part of DecodeAssist (on #PF/#NPF) X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=deb245252e80db10906d42dd831d1927ded9c294;p=xen.git svm: implement instruction fetch part of DecodeAssist (on #PF/#NPF) Newer SVM implementations (Bulldozer) copy up to 15 bytes from the instruction stream into the VMCB when a #PF or #NPF exception is intercepted. This patch makes use of this information if available. This saves us from a) traversing the guest's page tables, b) mapping the guest's memory and c) copy the instructions from there into the hypervisor's address space. This speeds up #NPF intercepts quite a lot and avoids cache and TLB trashing. Signed-off-by: Andre Przywara Signed-off-by: Keir Fraser --- diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index af903c9b72..1230de6031 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -996,6 +996,8 @@ int hvm_emulate_one( hvmemul_ctxt->insn_buf_eip = regs->eip; hvmemul_ctxt->insn_buf_bytes = + hvm_get_insn_bytes(curr, hvmemul_ctxt->insn_buf) + ? : (hvm_virtual_to_linear_addr( x86_seg_cs, &hvmemul_ctxt->seg_reg[x86_seg_cs], regs->eip, sizeof(hvmemul_ctxt->insn_buf), diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index 8753cafa3e..a0db4c0c6c 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -660,6 +660,21 @@ static void svm_set_rdtsc_exiting(struct vcpu *v, bool_t enable) vmcb_set_general1_intercepts(vmcb, general1_intercepts); } +static unsigned int svm_get_insn_bytes(struct vcpu *v, uint8_t *buf) +{ + struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; + unsigned int len = v->arch.hvm_svm.cached_insn_len; + + if ( len != 0 ) + { + /* Latch and clear the cached instruction. */ + memcpy(buf, vmcb->guest_ins, 15); + v->arch.hvm_svm.cached_insn_len = 0; + } + + return len; +} + static void svm_init_hypercall_page(struct domain *d, void *hypercall_page) { char *p; @@ -1650,6 +1665,7 @@ static struct hvm_function_table __read_mostly svm_function_table = { .msr_write_intercept = svm_msr_write_intercept, .invlpg_intercept = svm_invlpg_intercept, .set_rdtsc_exiting = svm_set_rdtsc_exiting, + .get_insn_bytes = svm_get_insn_bytes, .nhvm_vcpu_initialise = nsvm_vcpu_initialise, .nhvm_vcpu_destroy = nsvm_vcpu_destroy, @@ -1836,7 +1852,12 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs) (unsigned long)regs->ecx, (unsigned long)regs->edx, (unsigned long)regs->esi, (unsigned long)regs->edi); - if ( paging_fault(va, regs) ) + if ( cpu_has_svm_decode ) + v->arch.hvm_svm.cached_insn_len = vmcb->guest_ins_len & 0xf; + rc = paging_fault(va, regs); + v->arch.hvm_svm.cached_insn_len = 0; + + if ( rc ) { if ( trace_will_trace_event(TRC_SHADOW) ) break; @@ -2013,7 +2034,10 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs) case VMEXIT_NPF: perfc_incra(svmexits, VMEXIT_NPF_PERFC); regs->error_code = vmcb->exitinfo1; + if ( cpu_has_svm_decode ) + v->arch.hvm_svm.cached_insn_len = vmcb->guest_ins_len & 0xf; svm_do_nested_pgfault(v, regs, vmcb->exitinfo2); + v->arch.hvm_svm.cached_insn_len = 0; break; case VMEXIT_IRET: { diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h index 12bd8a82b1..1e5f7ec5fc 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -133,6 +133,9 @@ struct hvm_function_table { int (*cpu_up)(void); void (*cpu_down)(void); + /* Copy up to 15 bytes from cached instruction bytes at current rIP. */ + unsigned int (*get_insn_bytes)(struct vcpu *v, uint8_t *buf); + /* Instruction intercepts: non-void return values are X86EMUL codes. */ void (*cpuid_intercept)( unsigned int *eax, unsigned int *ebx, @@ -342,6 +345,11 @@ static inline void hvm_cpu_down(void) hvm_funcs.cpu_down(); } +static inline unsigned int hvm_get_insn_bytes(struct vcpu *v, uint8_t *buf) +{ + return (hvm_funcs.get_insn_bytes ? hvm_funcs.get_insn_bytes(v, buf) : 0); +} + enum hvm_task_switch_reason { TSW_jmp, TSW_iret, TSW_call_or_int }; void hvm_task_switch( uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason, diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h b/xen/include/asm-x86/hvm/svm/vmcb.h index f589bdf5ac..eecec70ce7 100644 --- a/xen/include/asm-x86/hvm/svm/vmcb.h +++ b/xen/include/asm-x86/hvm/svm/vmcb.h @@ -501,6 +501,9 @@ struct arch_svm_struct { int launch_core; bool_t vmcb_in_sync; /* VMCB sync'ed with VMSAVE? */ + /* VMCB has a cached instruction from #PF/#NPF Decode Assist? */ + uint8_t cached_insn_len; /* Zero if no cached instruction. */ + /* Upper four bytes are undefined in the VMCB, therefore we can't * use the fields in the VMCB. Write a 64bit value and then read a 64bit * value is fine unless there's a VMRUN/VMEXIT in between which clears